package transportP2P

import (
	"fmt"
	"gitee.com/jjawesomejj/awesome-throuh-networka/channel"
	"gitee.com/jjawesomejj/awesome-throuh-networka/define"
	"gitee.com/jjawesomejj/awesome-util/helper/commonhelper"
	"gitee.com/jjawesomejj/awesome-util/helper/timeHelper"
	"gitee.com/jjawesomejj/awesomehttp/system/helper"
	"sync"
	"time"
)

var controlPointer *channel.PanelControl
var udpMap sync.Map

func GetControlPointer() *channel.PanelControl {
	return controlPointer
}

func StartPointer(serverIp string, serverPortStr string, serverUdpPortStr string, uniqueId string, endChan chan bool, natType string) {
	retryTime := 0
	for retryTime <= 3 {
		retryTime++
		controlPanel := channel.Start(
			serverIp,
			serverPortStr,
			serverUdpPortStr,
			uniqueId,
			natType,
		)
		controlPanel.OnSuccessCreateWrite = func(conn *define.UdpConnection) {
			buff := make([]byte, bufferSize)
			n, err := conn.Read(buff)
			if err != nil {
				fmt.Println(err, "err")
			}
			var msg InitIpMsg
			err = commonhelper.JsonDecodeWithType(string(buff[:n]), &msg)
			if err != nil || msg.Command != INIT_TARGET_COMMAND {
				fmt.Println(err, "err")
				conn.Conn.Close()
			}
			manager.SetTransportUdp(msg.TargetId, conn, func(message []byte) {
				OnReadFromUdp(message, msg.TargetId)
			})
		}
		isRun := true
		for isRun {
			select {
			case <-controlPanel.Ready:
				controlPointer = controlPanel
				break
			case <-controlPanel.Finished:
				controlPointer = &channel.PanelControl{}
				isRun = false
				break
			}
		}
		time.Sleep(time.Second)
	}
	fmt.Println("结束循环啦")
	endChan <- true

}

func GetTargetUdpConnection(targetId string) (client *define.UdpConnection, err error) {
	if conn, ok := udpMap.Load(targetId); ok {
		return conn.(*define.UdpConnection), nil
	}
	client, err = controlPointer.GetTransportUdpClient(targetId, time.Second*5)
	if err != nil {
		udpMap.Store(targetId, client)
	}
	return client, err
}

type bufferControl struct {
	Conn                *define.UdpConnection
	Message             chan interface{}
	StopChanReader      chan bool
	StopChanWriter      chan bool
	BufferSize          int
	WriterCallBack      func(message []byte)
	lastWriterTimeStamp float64
	lastReadTimeStamp   float64
	StopPing            chan bool
}

type TransportManager struct {
	MessageConn map[string]*bufferControl
	locker      sync.Mutex
	BufferSize  int
}

func (manager *TransportManager) HasUdpHandler(targetSid string) bool {
	if _, ok := manager.MessageConn[targetSid]; ok {
		return true
	}
	return false
}

func (manager *TransportManager) SetTransportUdp(targetSid string, udpConnection *define.UdpConnection, onRead func(message []byte)) {
	manager.locker.Lock()
	manageChannel := make(chan interface{}, 1000)
	control := &bufferControl{
		Conn:           udpConnection,
		Message:        manageChannel,
		StopChanReader: make(chan bool, 1),
		StopChanWriter: make(chan bool, 1),
		StopPing:       make(chan bool, 1),
		WriterCallBack: onRead,
		BufferSize:     bufferSize,
	}
	manager.MessageConn[targetSid] = control
	manager.locker.Unlock()
	go func() {
		control.Run()
		manager.locker.Lock()
		delete(manager.MessageConn, targetSid)
		manager.locker.Unlock()
	}()
}

func (manager *TransportManager) PushMessage(targetId string, msg ClientMsgSendToTun) {
	if control, ok := manager.MessageConn[targetId]; ok {
		control.Conn.Conn.SetWriteDeadline(time.Now().Add(time.Second))
		_, err := control.Conn.Write([]byte(commonhelper.JsonEncode(msg)))
		if err != nil {
			fmt.Println("发送异常", err)
		}
	}
}

func (control *bufferControl) Run() {
	go control.Reader()
	go control.Writer()
	go control.Ping()
	control.lastReadTimeStamp = timeHelper.NowFloat()
	control.lastWriterTimeStamp = timeHelper.NowFloat()
	control.Monitor()
}

func (control *bufferControl) Stop() {
	control.StopChanReader <- true
	control.StopChanWriter <- true
	control.StopPing <- true
}

func (control *bufferControl) Monitor() {
	for true {
		time.Sleep(time.Second * 2)
		if control.lastReadTimeStamp <= helper.NowFloat()-10 {
			control.Stop()
			break
		}
		//if control.lastWriterTimeStamp <= helper.NowFloat()-10 {
		//	control.Stop()
		//	break
		//}
	}
}

func (control *bufferControl) Writer() {
	run := true
	for run {
		select {
		case message := <-control.Message:
			control.Conn.Conn.SetWriteDeadline(time.Now().Add(time.Second))
			_, err := control.Conn.Write([]byte(commonhelper.JsonEncode(message)))
			if err != nil {
				fmt.Println("使用udp发送错误:", err)
				run = false
			}
			control.lastWriterTimeStamp = timeHelper.NowFloat()
			break
		case <-control.StopChanWriter:
			run = false
			break
		}
	}
}

func (control *bufferControl) Ping() {
	run := true
	c := time.NewTicker(time.Second * 5)
	fmt.Println("pingMsg------------------->")
	for run {
		select {
		case _ = <-c.C:
			fmt.Println("开始使用定时器发送信息")
			control.Conn.Conn.SetWriteDeadline(time.Now().Add(time.Second))
			_, err := control.Conn.Write([]byte(commonhelper.JsonEncode(CreatePingMsg())))
			if err != nil {
				fmt.Println("定时消息发送失败：", err)
				run = false
			}
			break
		case <-control.StopPing:
			run = false
			break
		}
	}
}

func (control *bufferControl) Reader() {
	run := true
	go func() {
		for run {
			control.Conn.Conn.SetReadDeadline(time.Now().Add(time.Second * 15))
			buff := make([]byte, control.BufferSize)
			n, err := control.Conn.Read(buff)
			if err != nil {
				run = false
				return
			}
			control.lastReadTimeStamp = timeHelper.NowFloat()
			buff = buff[:n]
			control.WriterCallBack(buff)
		}
	}()
	select {
	case <-control.StopChanReader:
		run = false
		break
	}
}

func OnReadFromUdp(message []byte, srcId string) {
	messageStr := string(message)
	//fmt.Println("正常接受到消息:", messageStr)
	var commandMsg CommandMsg
	err := commonhelper.JsonDecodeWithType(messageStr, &commandMsg)
	if err != nil {
		fmt.Println("无效信息:", err)
		return
	}
	switch commandMsg.Command {
	case COMMAND_SEND_TO_TUN:
		var sendToTunMessage ClientMsgSendToTun
		err := commonhelper.JsonDecodeWithType(messageStr, &sendToTunMessage)
		if err != nil {
			fmt.Println("消息解析失败", err)
			return
		}
		sendToTunMessage.SrcSid = srcId
		OnReadMessage(commonhelper.JsonEncode(sendToTunMessage))
	}
}
